from types import MethodType
from corasy import settings
from w3lib.url import safe_url_string

from corasy.utils.tools import to_bytes


class Request(object):
    """
    用于封装用户请求信息所用
    """
    METHOD = ["GET", "POST"]

    def __init__(self, url: str, method: str = 'GET', callback: MethodType = None, data: dict = None, json: str = None,
                 meta: dict = None,
                 headers: dict = None, encoding='utf-8', cookies=None):
        self._encoding = encoding
        self.method = str(method).upper()
        if self.method not in self.METHOD:
            raise Exception(f"{self.method} method is not supported")

        self._set_url(url)
        if callback is not None and not callable(callback):
            raise TypeError(f'callback must be a callable, got {type(callback).__name__}')
        self.callback = callback
        self.data = self._set_data(data)
        self.json = json
        if headers and isinstance(headers, dict):
            self.headers = headers
        elif hasattr(settings, 'HEADERS') and isinstance(headers, dict):
            self.headers = settings.HEADERS
        else:
            self.headers = {}
        # else:
        # self.headers = settings if settings.headers else None
        self._meta = dict(meta) if meta else None
        self.cookie = cookies or {}

    @property
    def url(self):
        return self._url

    def _set_url(self, url):
        # 如果不是字符类型肯定报错
        if not isinstance(url, str):  # 如果不是字符串 就报错 提醒用户
            raise TypeError(f'Request url must be str or unicode, got {type(url).__name__}')

        # 如果没有冒号 肯定不是完整的网址
        self._url = safe_url_string(url, self.encoding)
        if (
                '://' not in self._url
                and not self._url.startswith('about:')
                and not self._url.startswith('data:')
        ):
            raise ValueError(f'Missing scheme in request url: {self._url}')

    @property
    def meta(self):
        if self._meta is None:
            self._meta = {}
        return self._meta

    @property
    def encoding(self):
        return self._encoding

    # 表单的设置 主要用于POST表单
    def _set_data(self, data):
        '''
        表单数据的设置 主要用于POST表单 将数据转换成字节的形式
        :param data: POST的数据
        :return: 返回POST的表单数据(字节形式)
        '''
        # 如果POST的表单数据没有指定就是空值
        if data is None:  # 设置为空
            self.data = b''  # 设置为空字节
        else:
            # 如果有数据 就将其转化成字节的形式 并赋予编码 默认utf-8
            self.data = to_bytes(data, self.encoding)
        # 返回字节数据
        return self.data

    def __str__(self):
        '''
        用于打印请求对象
        :return: 返回请求方法 请求的网址
        '''
        return f"<Response url[{self.method}]: {self._url}"
